我們所熟知的Http server-client架構一向都是由client向server發送請求,server再吐回對應的內容。
那假如今天我們希望能在server資料庫被更動時立即通知client端,即時更新使用者介面,除了client端不斷的輪詢來取得後端資料外,是否有更高端、有效率的方式?
我們可以透過WebSocket實作上述的功能,Laravel中的BroadCast支援Redis和Pusher這兩個服務,而這篇文章會示範如何透過簡單的pusher來完成主動發送事件內容到前端畫面
先在eventServiceProvider中加入
        'App\Events\Event名稱' => [
                'App\Listeners\Event名稱',
        ],
範例--新增食物:
 'App\Events\FoodAdded' => [
            'App\Listeners\FoodAddedListener',
        ],
接著用artisan產生event $php artisan event:generate
event class裡描述的是該事件的內容,事件如何才會被觸發則是在controller中被定義
範例是新增食物主動推播到訂閱指定餐廳的頻道的事件
專案目錄下的app/Event/FoodAdded.php
<?php
namespace App\Events;
//加入會用到的model
use App\Food;
use App\Restaurant;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class FoodAdded implements ShouldBroadcast //若是主動發送推播通知的事件,這邊要改成ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;
    public $restaurant;
    public $food;
    public $class = 'food added';
    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(Food $food)
    {   //這邊描述的是推播通知的內容
        $this->food = $food;
        $this->restaurant = Restaurant::find($food->restaurant_id);
    }
    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        //定義新增食物的通知只會發送到指定的餐廳
        return ['food-channel.'.$this->restaurant->id];
    }
    public function broadcastAs()
    {
        //命名推播的事件
        return 'food-event';
    }
}
FoodController.php
function store(Request $request)
    {
    //...
       $food = Food::create([
                'name' => $request->name,
                'remaining' => $request->remaining,
                'original_price' => $request->original_price,
                'discounted_price' => $request->discounted_price,
                'image' => $parameters['image'],
                'restaurant_id' => $request->restaurant_id,
            ]);
        //每當該餐廳有食物上架時,會觸發FoodAdded事件,發送推播通知訂閱該餐廳的頻道
        event(new FoodAdded($food));
        return response()->json($food, 200);
    }
此範例用Pusher來實作推播通知
首先要在專案目錄下安裝pusher套件 composer require pusher/pusher-php-server
接著更改.env內容:
BROADCAST_DRIVER=pusher
//以下欄位要先去pusher官網申請一組專案,申請完後即可在官網拿到以下四個欄位
PUSHER_APP_ID='你的pusher app id'
PUSHER_APP_KEY='你的pusher app key'
PUSHER_APP_SECRET='你的pusher app secret'
PUSHER_APP_CLUSTER='你的pusher app cluster'
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
Pusher官網有提供一個簡單的blade範例來檢查自己寫的推播通知事件是否能成功發送
<!DOCTYPE html>
<head>
    <title>New Food Pusher Test</title>
    <script src="https://js.pusher.com/5.1/pusher.min.js"></script>
    <script>
        var restaurant_id = 2; //測試2號餐廳食物上架時能不能收到主動發送的通知
        // Enable pusher logging - don't include this in production
        Pusher.logToConsole = true;
        var pusher = new Pusher('{{env('MIX_PUSHER_APP_KEY')}}', {
            cluster: '{{env('MIX_PUSHER_APP_CLUSTER')}}',
            forceTLS: true
        });
        var channel = pusher.subscribe('food-channel.'+restaurant_id);
        channel.bind('food-event', function(data) {
            console.log('Pusher');
            alert(JSON.stringify(data));
        });
    </script>
</head>
<body>
<h1>New Food Pusher Test</h1>
<p>
    Try publishing an event to channel <code>food-channel+restaurant_id</code>
    with event name <code>food-event</code>.
    <script>
    </script>
</p>
</body>
pusher.min.js:8 Pusher : : [{"type":"WebSocketError","error":{"type":"PusherError","data":{"code":4005,"message":"Path not found"}}}]
Solution:
.env要加上
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
2.前端看不到pusher該顯示的資料
Solution:檢查 .env
BROADCAST_DRIVER=pusher